#include "GUITree.h"
#include "IRenderer.h"
#include "OnMouseLeftDownEvent.h"
#include "OnMouseLeftUpEvent.h"
#include "OnMouseMoveEvent.h"

#define TEXTHEIGHT	10
#define INDENT		10
#define LINELENGTH	80

GUITree::GUITree()
{
	m_fontColor = 0x00000000;
	m_font = HashedString("Arial");
	m_visible = true;
	m_enabled = true;
	m_dimensions = Maths::Point<unsigned int>(LINELENGTH, TEXTHEIGHT);
	m_dragging = false;
}

void GUITree::Title( std::string& title )
{
	m_title = title;
}
std::string GUITree::Title() const
{
	return m_title;
}
void GUITree::Font( std::string& font )
{
	m_font = HashedString(font);
}
void GUITree::Font( HashedString& font )
{
	m_font = font;
}
HashedString GUITree::Font() const
{
	return m_font;
}
void GUITree::FontColor( unsigned int color )
{
	m_fontColor = color;
}
unsigned int GUITree::FontColor() const
{
	return m_fontColor;
}
void GUITree::Background( std::string& background )
{
	m_background = HashedString(background);
}
void GUITree::Background( HashedString& background )
{
	m_background = background;
}
HashedString GUITree::Background() const
{
	return m_background;
}
Pointer<GUITreeNode>& GUITree::Selected()
{
	return m_selected;
}
void GUITree::DrawNode( GUITreeNode* node, Maths::Point<unsigned int>& drawLocation, int indent )
{
	if (node == m_selected.getPointer())
		Renderer->PrintFont(m_font, m_fontColor, drawLocation.X + indent - 5, drawLocation.Y + 2, "*"); 
	Renderer->PrintFont(m_font, m_fontColor, drawLocation.X + indent, drawLocation.Y, node->Title().c_str());
	drawLocation.Y += TEXTHEIGHT;

	if (node->Open())
	{
		for (GUITreeNode::Vector_t::iterator it = node->Children().begin(); it != node->Children().end(); ++it)
		{
			DrawNode((*it), drawLocation, indent + INDENT);
		}
	}
}
void GUITree::Paint()
{
	if (m_visible)
	{
		Maths::Point<unsigned int> drawLocation(m_location.X + m_parentLocation.X, m_location.Y + m_parentLocation.Y);

		Renderer->PrintFont(m_font, m_fontColor, drawLocation.X, drawLocation.Y, m_title.c_str());
		drawLocation.Y += TEXTHEIGHT;

		if (m_isOpen)
		{
			for (GUITreeNode::Vector_t::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
			{
				DrawNode((*it), drawLocation, INDENT);
			}
		}
	}
}

bool GUITree::ReceiveEvent( IEvent& event )
{
	if (m_visible && m_enabled)
	{
		if (event.Type() == OnMouseLeftUpEvent::type)
		{
			OnMouseLeftUpEvent& mouseEvent = static_cast<OnMouseLeftUpEvent&>(event);
			
			int x = mouseEvent.Coords().X;
			int y = mouseEvent.Coords().Y;
			int absX = m_location.X + m_parentLocation.X;
			int absY = m_location.Y + m_parentLocation.Y;
			
			if ((x > absX) && (x < absX + (int)m_dimensions.X) &&
				(y > absY) && (y < absY + (int)m_dimensions.Y))				// Collided With me
			{
				if (!m_dragging)
				{
					m_isOpen = !m_isOpen;										// toggle
				} else
				{
					m_finalDropLocation = 0;
					(*m_onDragDropSelected)();
					m_dragging = false;										// Mouse released, stop dragging
				}
				return true;												// consume
			}  else
			{
				if (m_isOpen)												// Child nodes available?
				{
					Maths::Point<unsigned int> currLoc(absX, absY+TEXTHEIGHT);

					for (GUITreeNode::Vector_t::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
					{
						if (ReceiveMouseUpEvent((*it), currLoc, mouseEvent.Coords()))
						{
							m_dragging = false;								// Mouse released, stop dragging
							return true;
						}
					}
				}
			}
		} else if (event.Type() == OnMouseLeftDownEvent::type)
		{
			OnMouseLeftDownEvent& mouseEvent = static_cast<OnMouseLeftDownEvent&>(event);
			
			int x = mouseEvent.Coords().X;
			int y = mouseEvent.Coords().Y;
			int absX = m_location.X + m_parentLocation.X;
			int absY = m_location.Y + m_parentLocation.Y;
		
			if ((x > absX) && (x < absX + m_dimensions.X) &&
				(y > absY) && (y < absY + m_dimensions.Y))				// Collided With me
			{
				m_selected = 0;
				if (m_onChangeSelected.isValid())
					(*m_onChangeSelected)();
				return true;												// consume
			}  else
			{
				if (m_isOpen)												// Child nodes available?
				{
					Maths::Point<unsigned int> currLoc(absX + INDENT, absY+TEXTHEIGHT);

					for (GUITreeNode::Vector_t::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
					{
						if (ReceiveMouseDownEvent((*it), currLoc, mouseEvent.Coords()))
							return true;
					}
				}
			}
		} else if (event.Type() == OnMouseMoveEvent::type)
		{
			OnMouseMoveEvent& mouseEvent = static_cast<OnMouseMoveEvent&>(event);
			if (mouseEvent.LButton())				// left drag
			{
				m_dragging = true;
			} else
			{
				m_dragging = false;
			}
		}
	}
	return false;
}
bool GUITree::ReceiveMouseUpEvent( Pointer<GUITreeNode>& node, Maths::Point<unsigned int>& location, Maths::Point<int>& click )
{
	int absX = location.X;
	int absY = location.Y;
	
	location.X;
	location.Y += TEXTHEIGHT;

	if ((click.X > absX) && (click.X <= absX + LINELENGTH + INDENT) &&
		(click.Y > absY) && (click.Y <= absY + TEXTHEIGHT))					// Collide with node?
	{
		if (!m_dragging)
		{
			node->Open(!node->Open());
		} else
		{
			m_finalDropLocation = node;
			(*m_onDragDropSelected)();
			m_dragging = false;
		}
		return true;
	} else if (node->Open())
	{
		for (GUITreeNode::Vector_t::iterator it = node->Children().begin(); it != node->Children().end(); ++it)
		{
			if (ReceiveMouseUpEvent((*it), location, click))
			{
				return true;
			}
		}
	}
	return false;
}
bool GUITree::ReceiveMouseDownEvent( Pointer<GUITreeNode>& node, Maths::Point<unsigned int>& location, Maths::Point<int>& click )
{
	int absX = location.X;
	int absY = location.Y;
	
	location.X;
	location.Y += TEXTHEIGHT;

	if ((click.X > absX) && (click.X < absX + LINELENGTH + INDENT) &&
		(click.Y > absY) && (click.Y < absY + TEXTHEIGHT))					// Collide with node?
	{
		m_selected = node;
		
		if (m_onChangeSelected.isValid())
			(*m_onChangeSelected)();
		return true;
	} else if (node->Open())
	{
		for (GUITreeNode::Vector_t::iterator it = node->Children().begin(); it != node->Children().end(); ++it)
		{
			if (ReceiveMouseDownEvent((*it), location, click))
				return true;
		}
	}
	return false;
}

void GUITree::AddChildNode( Pointer<GUITreeNode>& child )
{
	m_nodes.push_back(child);
}
Pointer<GUITreeNode> GUITree::FindChildNode( GUITreeNode* child )	// Takes a whil
{
	return 0;
}
void GUITree::RemoveChild( unsigned int index )
{
	m_nodes.erase(m_nodes.begin() + index);
}
void GUITree::RemoveChild( GUITreeNode* child )
{
	for (int i = 0; i < m_nodes.size(); ++i)
	{
		if (m_nodes[i].getPointer() == child)
		{
			m_nodes.erase(m_nodes.begin() + i);
			return;
		}
	}
}
GUITreeNode::Vector_t& GUITree::Children()
{
	return m_nodes;
}

void GUITree::Open( bool open )
{
	m_isOpen = open;
}
bool GUITree::Open() const
{
	return m_isOpen;
}
void GUITree::OnChangeSelected( Pointer<Functor>& func )
{
	m_onChangeSelected = func;
}
void GUITree::OnDragDropSelected( Pointer<Functor>& func )
{
	m_onDragDropSelected = func;
}
Pointer<GUITreeNode> GUITree::FinalDropLocation()
{
	return m_finalDropLocation;
}